import type { TransformPluginContext, TransformResult } from 'rollup'

import { simple } from 'acorn-walk'

import { ImportsProcessor } from './importsProcessor'
import { isIdentifier, isMemberExpression } from './utils/ast'

interface ReplaceCode {
  currentCode: string
  fnName: string
}

export class Transformer {
  async replaceCode(context: TransformPluginContext, code: string, id: string): Promise<TransformResult> {
    if (!/\.[jt]s$/i.test(id))
      return

    const ast = context.parse(code)

    const imports = new ImportsProcessor(context)

    // 收集需要替换
    const replaceCode: ReplaceCode[] = []

    simple(ast, {
      // 收集函数导入信息
      ImportDeclaration: (node) => {
        imports.append(node)
      },

      // 函数调用位置: 找到toString()调用并替换为字符串
      CallExpression: async (node) => {
        const { callee, start, end } = node

        // 替换为字符串字面量
        const currentCode = code.slice(start, end)

        // 如果数组已存在 则跳过
        if (replaceCode.some(i => i.currentCode === currentCode))
          return

        // 被调用的函数名 可以捕获到toString()调用
        const name = isMemberExpression(callee) && isIdentifier(callee.property)
          ? callee.property.name
          : ''

        if (name !== 'toString')
          return

        // 被调用toString()的对象名 即要找的函数名
        const fnName = isMemberExpression(callee) && isIdentifier(callee.object)
          ? callee.object.name
          : ''

        if (!fnName)
          return
        replaceCode.push({
          currentCode,
          fnName,
        })
      },
    })

    for (const { currentCode, fnName } of replaceCode) {
      const newCode = await imports.findFnDeclarationByFnName(fnName)
      if (newCode) {
        code = code.replaceAll(currentCode, `'${newCode}'`)
      }
    }

    return { code, map: null }
  }
}
